講完了怎麼取 tag 的值,我們來取 tag 夾帶的屬性。假設我們拿到了下面的 RSS 內容:
<channel>
<category>channel category 1</category>
<category domain="http://channel.category.domain">channel category 2</category>
</channel>
首先我們先取 <category>
的 attribute ,但我們在處理 parser 的時候有個基本原則就是不能夠預設資料一定存在,所以 null safty 處理在這邊是需要非常小心!對 Element
取值只要用原生的 function getAttribute
就好了。
fun Element.getAttributeOrNull(tag: String): String? {
val attr = getAttribute(tag) ?: return null
return if (attr.isEmpty() || attr.isBlank()) null else attr
}
接著我們可以來處理怎麼拿到 <category>
的資訊,上面的情況我們拿到讀 category 不只一個,而且內容物不一定相同。我可以自己定義一個 data class 去包這些資訊,但他內含的資訊有可能是 nullable 的。
data class Category(
val name: String?,
val domain: String?,
)
之後我們就可以把上面的東西整合一下,寫出一個專門讀 <category>
列表的 extension function 。
fun Element.readCategories(parentTag: String, tagName: String): List<Category>? {
val result = mutableListOf<Category>()
val nodeList = getElementsByTagName(tagName) ?: return null
for (i in 0 until nodeList.length) {
val e = nodeList.item(i) as? Element ?: continue
val parent = e.parentNode as? DeferredElementImpl
if (parent?.tagName == parentTag || parent?.tagName == tagName) {
result.add(Category(name = e.getAttributeOrNull(TEXT), domain = null))
}
}
return result
}
那上面為什麼又需要 parentTag
呢?因為 category 這個 tag 有可能存在在其他的子節點裡,比方說像下面這樣:
<channel>
<category>channel category 1</category>
<category domain="http://channel.category.domain">channel category 2</category>
<item>
<category>item category 1</category>
</item>
<item>
<category>item category 2</category>
</item>
</channel>
總結一下目前我們寫的 Custom DOM Parser 做的事情:
現在這種實作方式,每遇到一種樣子的 tag ,我們就有可能需要多寫一個 function 和多定義一個 data class 去包裡面的資訊。Kotlin 的實作方式可以讓我們在有支援 Kotlin 的平台上,去操作這些 parser ,但我們也可以透過平台專屬的 API 去優化我們的 parser ,下篇文章我們就會開始以 Android 為例去優化我們的 Kotlin parser 。